package com.lanhai.longheng.dispatch;
 
 

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;

import com.lanhai.longheng.MsgCore; 
import com.lanhai.longheng.beans.AgvBean;
import com.lanhai.longheng.beans.AgvDataBean;
import com.lanhai.longheng.beans.AgvMountBean;
import com.lanhai.longheng.beans.DeviceBean;
import com.lanhai.longheng.beans.DotBean; 
import com.lanhai.longheng.dao.DeviceGroupDao;
import com.lanhai.longheng.dao.QRAllowLeaveDao;
import com.lanhai.longheng.dao.RoadDeviceDao;
import com.lanhai.longheng.enums.AgvCurrentTransEnum;
import com.lanhai.longheng.enums.AgvMountTypeEnum;
import com.lanhai.longheng.enums.AgvReceiveEnum;
import com.lanhai.longheng.enums.DotTypeEnum; 
import com.lanhai.longheng.interfaces.IAgv; 
import com.lanhai.longheng.interfaces.IAgvEvent;
import com.lanhai.longheng.interfaces.IAgvMount;
import com.lanhai.longheng.interfaces.ICmdLog;
import com.lanhai.longheng.interfaces.IControlArea;
import com.lanhai.longheng.interfaces.IDevice;
import com.lanhai.longheng.interfaces.IDeviceProtocol;
import com.lanhai.longheng.interfaces.IDot;
import com.lanhai.longheng.interfaces.IDotEvent;
import com.lanhai.longheng.interfaces.IPower;
import com.lanhai.longheng.interfaces.ISendData;
import com.lanhai.longheng.interfaces.ITempData; 
import com.lanhai.longheng.mapper.RoadMapper;
import com.lanhai.longheng.report.AgvStateMap;
import com.lanhai.longheng.third.DeviceFactory;
import com.lanhai.longheng.utils.Tools; 

/**
 * 小车的消息处理
 * @author zhengwei
 *
 */
@EnableAsync 
@Service
public class DispatchAgv {
 
	
	
	@Autowired
	IAgv iAgv;
	
	@Autowired
	IAgvMount iAgvMount;
	
	@Autowired
	IAgvEvent iAgvEvent;
	
	@Autowired
	ITempData iTempData;
	
	@Autowired
	IDot iDot; 
	
	@Autowired
	IDotEvent iDotEvent;
	
	@Autowired
	IDevice iDevice;
	
	@Autowired
	IControlArea iControlArea;
	   

	@Autowired
	ISendData sendTools;
	
	@Autowired
	ICmdLog iCmdLog; 
	 
	@Autowired
	RoadMapper roadMapper;
	
	@Autowired
	IPower iPower;
	
	 
	 private static final Logger logger =  LogManager.getLogger("agvlog");
	
	
	@Async("agvExecutor") 
	public  void resolve(AgvDataBean data) {
		 
		// System.out.println("DispatchAgv : " + Thread.currentThread().getName() + "   " + UUID.randomUUID().toString());
	  
		AgvBean agv=iAgv.getBean( data.getAgvId()); 
		if (agv == null) {
			// 对应的小车不存在 
			logger.error("小车不存在"+data.getAgvId()+"  cmd="+data.getCmdStr());  
			return;
		} 
		DotBean dot = iDot.getBeanByXY(data.getX(), data.getY());
	
		
		//优先级高的处理
		if(data.getReceiveType()==AgvReceiveEnum.JT  ) {
			iCmdLog.addAgvReceiveLog(data.getAgvId(), data.getReceiveType().getName(), data.getCmdStr());
			//处理机台处AGV急停
			if(dot!=null) {
				DeviceBean cudevice=iDevice.getDaoByDot(dot.getDotNO(),true); 
				if(cudevice!=null) {
					//发送AGV异常
					IDeviceProtocol deviceEvent = DeviceFactory.getDevice(cudevice.getCmdSign()); 
					sendTools.sendDeviceData(cudevice.getIp(), cudevice.getPort(), cudevice.getLine(), "异常", deviceEvent.agvError(cudevice.getLine()),cudevice.getDeviceGroup());
				} 
			} 
			AgvStateMap.saveState(data,agv);
			AgvCmdMap.setDispatch(agv.getAgvId(), false);
			return; 
		}else if(data.getReceiveType() ==AgvReceiveEnum.TZ ||data.getReceiveType() ==AgvReceiveEnum.LEAVEDEVICE ) {
			iCmdLog.addAgvReceiveLog(data.getAgvId(), data.getReceiveType().getName(), data.getCmdStr());
			//处理机台前的停止和手动回退
			if(dot!=null&& dot.getDotType()==DotTypeEnum.Trans) { 
				DeviceBean cudevice=iDevice.getDaoByDot(dot.getDotNO(),true);
				if(cudevice!=null) {
					//发送AGV异常
					IDeviceProtocol deviceEvent = DeviceFactory.getDevice(cudevice.getCmdSign()); 
					sendTools.sendDeviceData(cudevice.getIp(), cudevice.getPort(), cudevice.getLine(), "异常", deviceEvent.agvError(cudevice.getLine()),cudevice.getDeviceGroup());
				} 
			}
			AgvStateMap.saveState(data,agv);
			AgvCmdMap.setDispatch(agv.getAgvId(), false);
			return;  
		}
		
		
		
		if(AgvCmdMap.isDispatch(agv.getAgvId())) {
			//正在处理中，不操作
			iCmdLog.addAgvReceiveLog(data.getAgvId(), data.getReceiveType().getName(), data.getCmdStr());
			String msge=String.format("AGV：[%s],正在处理%s，无法处理新的请求:%s",agv.getAgvId(),agv.getCurrentDo(),
					data.getReceiveType().getName());
			 logger.error(msge);
			return; 
		}
		AgvCmdMap.setDispatch(agv.getAgvId(), true);
		
		AgvMountBean mobj = new AgvMountBean(); 
		mobj.setDownFull(data.isDownFull());
		mobj.setUpFull(data.isUpFull()); 
		mobj.setMountType(data.getMountType()); 
		agv.setCurrentDo(data.getReceiveType().getName());
		
		switch (data.getReceiveType()) {
		case BZ:
			//// 避障 nothing 
			break;
		case CD:
			//// 充电 nothing 
			break;
		case DD:
			//// 到达 
			iCmdLog.addAgvReceiveLog(data.getAgvId(), data.getReceiveType().getName(), data.getCmdStr()); 
			arrived(agv,data,mobj,dot); 
			break;
		case DM:
			// 待命 nothing 
			break;
		case JG:
			// 交管 nothing 
			break; 
		case KCS:
			// 可传输,发起调度 
			if(AgvCmdMap.isTransing(agv.getAgvId())==false){ 
				iCmdLog.addAgvReceiveLog(data.getAgvId(), data.getReceiveType().getName(), data.getCmdStr()); 
				// 发起调度
				noticeJudgement(agv ,data.getX(),data.getY(),false,mobj); 
			} else {
				//传输中，不处理。退回二维码后清空 
				MsgCore.sendMsg(agv.getAgvId(), "正在传输中");
			}
			break;
		case LX:
			// 离线 
			break;
		case SCCW: 
			//上层传出完成 
			iCmdLog.addAgvReceiveLog(data.getAgvId(), data.getReceiveType().getName(), data.getCmdStr());  
			agv.setUpHasTrans(true);
			if(agv.isDownHasTrans()) {
				agv.setFinishTrans(true);
			}
			agv.setCurrentTrans(AgvCurrentTransEnum.None);
			iAgv.saveCache(agv);
			 
			//调度  
			noticeJudgement(agv ,data.getX(),data.getY(),true,mobj); 
			break;
		case SCRW:
			//上层传入完成 
			iCmdLog.addAgvReceiveLog(data.getAgvId(), data.getReceiveType().getName(), data.getCmdStr());  
			agv.setUpHasTrans(true);
			if(agv.isDownHasTrans()) {
				agv.setFinishTrans(true);
			}
			agv.setCurrentTrans(AgvCurrentTransEnum.None);
			iAgv.saveCache(  agv);
			 
			//调度
			noticeJudgement(agv ,data.getX(),data.getY(),true,mobj); 
			break;
		case SCXRW:
			//上出下入完成 
			iCmdLog.addAgvReceiveLog(data.getAgvId(), data.getReceiveType().getName(), data.getCmdStr()); 
			agv.setUpHasTrans(true);
			agv.setDownHasTrans(true);
			agv.setFinishTrans(true);
			agv.setCurrentTrans(AgvCurrentTransEnum.None);
			iAgv.saveCache(agv);
			 
			//调度
			noticeJudgement(agv ,data.getX(),data.getY(),true,mobj); 
			break; 
		case SRXCW:
			//上入下出完成 
			iCmdLog.addAgvReceiveLog(data.getAgvId(), data.getReceiveType().getName(), data.getCmdStr());  
			agv.setUpHasTrans(true);
			agv.setDownHasTrans(true);
			agv.setFinishTrans(true);
			agv.setCurrentTrans(AgvCurrentTransEnum.None);
			iAgv.saveCache(  agv);
			  
			//调度
			noticeJudgement(agv ,data.getX(),data.getY(),true,mobj); 
			break; 
		case XCCW:
			//下层传出完成
			iCmdLog.addAgvReceiveLog(data.getAgvId(), data.getReceiveType().getName(), data.getCmdStr());  
			agv.setDownHasTrans(true);
			if(agv.isUpHasTrans()) {
				agv.setFinishTrans(true);
			}
			agv.setCurrentTrans(AgvCurrentTransEnum.None);
			iAgv.saveCache(  agv); 
			
			//调度
			noticeJudgement(agv ,data.getX(),data.getY(),true,mobj); 
			break;
		case XCRW:
			//下层传入完成 
			iCmdLog.addAgvReceiveLog(data.getAgvId(), data.getReceiveType().getName(), data.getCmdStr());  
			agv.setDownHasTrans(true); 
			if(agv.isUpHasTrans()) {
				agv.setFinishTrans(true);
			}
			agv.setCurrentTrans(AgvCurrentTransEnum.None);
			iAgv.saveCache(agv);
			 
			//调度
			noticeJudgement(agv ,data.getX(),data.getY(),true,mobj); 
			break; 
		case ZXWC:
			// 装卸完成 nothing
			break; 
		case ERR:
			// 异常了 
			break;
		case XS:
			// 行驶中
			moving(agv, data);
			break;
		default:
			break; 
		}
		 
		AgvStateMap.saveState(data,agv);
		AgvCmdMap.setDispatch(agv.getAgvId(), false);
	}
	
	/**
	 * 发起调度判断
	 * @param agvid
	 * @param dotNO
	 */
	public void noticeJudgement(AgvBean agv, int x, int y,boolean transEnd,AgvMountBean mobj) {
		DotBean bean = iDot.getBeanByXY(x, y);
		 
		if (bean != null) {
			MsgCore.sendMsg(agv.getAgvId(), "等待机台传输");
			DeviceBean device=iDevice.getDaoByDot(bean.getDotNO(),false);
			if(device!=null) { 
				iAgvMount.judgement(agv, device ,bean ,mobj);
			}else {
				MsgCore.sendMsg(agv.getAgvId(), "错误的对接工位，该工位没配设备");
				logger.info("错误的对接工位，该工位没配设备 "+bean.getDotNO());
			}  
		} else {
			MsgCore.sendMsg(agv.getAgvId(), "错误的对接工位");
			logger.info("错误的对接工位 x,y没有对应的工位");
		}
	}
	  
	public void moving(AgvBean agv,AgvDataBean data) {
		 
		long areaID = iControlArea.getAreaId(data.getX(), data.getY());
		if (areaID > 0) {
			if (agv.getAreaId() == areaID) {
				//小车自身锁区域，不处理
				return;
			} 
			if(agv.getAreaId()>0&&agv.getAreaId()!=areaID) {
				//从一个区离开极短时间到另外一个区，需要释放原区
				iControlArea.unlockArea(agv.getAreaId());   
			}
		 
			// 进入交管区
			agv.setAreaId(areaID); 
			iAgv.saveCache(agv);  
			
			if (iControlArea.lockArea(areaID, agv.getAgvId())) {
				// 锁住区域成功,更新当前区域id  继续前进   
				return; 
			} else {
				// 不能通行，需要停下来 
				if(iControlArea.addStopAgv(areaID, agv.getAgvId())) { 
					MsgCore.sendMsg(agv.getAgvId(), "交管中，小车暂不能通行");
					// 向小车发送停车的指令
					iAgvEvent.stopOrstart(agv,   true); 
				}  
			}
		} else {
			// 离开交管区 
			if (agv.getAreaId() > 0) { 
				iControlArea.unlockArea(agv.getAreaId());  
				MsgCore.sendMsg(agv.getAgvId(), "目标机台："+agv.getNextNode()); 
				agv.setAreaId(0);
				iAgv.saveCache(agv);  
				return;
			}
		} 
	}
	 
    void arrived(AgvBean agv,AgvDataBean data,AgvMountBean mobj,DotBean cDot) { 
		// 位置没变化才操作
		// 更新小车信息
		agv.setCurrentX(data.getX());
		agv.setCurrentY(data.getY());
		if (cDot == null) {
			// 到达了一个表中不存在的停泊位，这是一个数据BUG 
			MsgCore.sendMsg(agv.getAgvId(), String.format("AGV到达了一个错误的点位:X=%d,Y=%d", data.getX(), data.getY()));
			return;
		}
 
		//只要是到达，强制清空
	    agv.setFinishTrans(false);
		agv.setUpHasTrans(false);
		agv.setDownHasTrans(false); 
		iAgv.saveCache(agv);
 
	 
		//// 开始处理停泊位的各种类型
	    
		if (cDot.getDotType() == DotTypeEnum.Power) {
			//// 充电点   
			MsgCore.sendMsg(agv.getAgvId(), "开始充电" );
			 iDotEvent.arrivedPower(agv, cDot);  
			 agv.setNextDeviceid(0);
			 agv.setNextNode(""); 
			 iAgv.saveCache(agv);
			 iPower.agvArrivedPower(agv.getAgvId(),cDot.getDotNO());
		} else if (cDot.getDotType() == DotTypeEnum.Wait) {
			//// 待命点 
			MsgCore.sendMsg(agv.getAgvId(), "待命中，目标工序："+Tools.getProcess(agv.getNextProcess()) );
			 iDotEvent.arrivedWait(agv, cDot);
			 
		} else if (cDot.getDotType() == DotTypeEnum.Trans) {
			//// 可传输工位 
			  
			 iDotEvent.arrivedTrans(agv, cDot);
			
			//放辆充电的小车
			 iPower.agvBackToWork(20);
		} else if (cDot.getDotType() == DotTypeEnum.Decide) {
			////决策点 
			 iDotEvent.arrivedDecide(agv, cDot, data);  
		}else if(cDot.getDotType()==DotTypeEnum.Route) {
			//路由点，直接行始小车
			if(cDot.getRouteRoad()>0) { 
				iAgvEvent.toRoad(agv ,   cDot.getRouteRoad());  
			}else {
				//没有分配路由 
				MsgCore.sendMsg(agv.getAgvId(), "点"+ cDot.getDotNO()+ "没有分配路由");
				logger.error("点"+ cDot.getDotNO()+ "没有分配路由");
			}
		}else if(cDot.getDotType()==DotTypeEnum.QrBefore) {
			//先给机台发离开的消息
			int transdot = iDot.getTransDotNo(cDot.getDotNO());
			DeviceBean cudevice=iDevice.getDaoByDot(transdot,true);
			if(cudevice!=null) {
				//发送AGV异常
				IDeviceProtocol deviceEvent = DeviceFactory.getDevice(cudevice.getCmdSign()); 
				sendTools.sendDeviceData(cudevice.getIp(), cudevice.getPort(), cudevice.getLine(), "异常", deviceEvent.agvError(cudevice.getLine()),cudevice.getDeviceGroup());
			
				//重置机台传输数据
				DeviceCmdMap.clearState(cudevice.getDeviceid());
			} 
		
			
			//判断小车的状态，不满足不准走
			if(Tools.agvCanLeave(agv.getNextProcess(), mobj)==false) {
				MsgCore.sendMsg(agv.getAgvId(), "花篮状态不对，不能去下道工序");
				return;
			}
			
		
			
			//再处理二维码的事 
			if(agv.isFinishTrans()) {
				if(mobj.getMountType()==AgvMountTypeEnum.Error) {
					//错误的装卸情况，比如上X下空，上满下X，不处理
					MsgCore.sendMsg(agv.getAgvId(), "花篮数量异常，暂停");
					return;
				}  
				//传输完成到达QR，在QR处释放
				iDotEvent.arrivedQR(agv,cDot);
			}else { 
				//AGV没有完成传输就退回了二维码   
				if(mobj.getMountType()==AgvMountTypeEnum.Error) {
					//错误的装卸情况，比如上X下空，上满下X，不处理
					MsgCore.sendMsg(agv.getAgvId(), "花篮数量异常，暂停");
					return;
				} 
			 
				//特别二维码的点位处理
				QRAllowLeaveDao qla=iDot.getAllowLeave(cDot.getDotNO());
				if(qla!=null) {
					AgvMountTypeEnum right=AgvMountTypeEnum.getAgvMountTypeEnumByCode(qla.getAllow());
					if(mobj.getMountType()==right) {
						if(qla.isIscheck()&&qla.getNextprocess()!=null) {
							//需要找下道工序,找到后不操作后面的
							if(toNextProcess(agv,agv.getSpecialline(),qla.getNextprocess(),cDot.getDotNO())) {
								//跳出，不需要再操作
								return;
							}
						}else {
							//不需要找工序，交给后续二维码去处理
							logger.info("不需要找工序，交给后续二维码去处理 dotno="+cDot.getDotNO()+"  nextprocess="+qla.getNextprocess());
						} 
					}else {
						//状态不对，不准动
						MsgCore.sendMsg(agv.getAgvId(), "上下装料情况不对，不能移动");
						return;
					} 
				}
				 
				 
				if(cDot.getRouteRoad()>0) {
					//二维码本身就有路由
					if (transdot > 0) {
						iTempData.setDotOfAgv(transdot, 0);   
					} 
					//路由路线，AGV涉及的机台全解锁
					iDevice.unlockForAgv(agv.getAgvId());
					iAgvEvent.toRoad(agv,cDot.getRouteRoad());  
				}else if(agv.getNextRoad()>0) {
					// *********  其实，这个判断极大可能不会进来
					if (transdot > 0) {
						iTempData.setDotOfAgv(transdot, 0);  
						//AGV有目标的，只解锁二维码对应机台的
						iDevice.unlockDeviceForDot(transdot,agv.getAgvId());
					} 
					iAgvEvent.toRoad(agv,agv .getNextRoad());  
				}else {
					// ******  这个判断也极大可能不会进来，在特别二维码的点位处理的时候就已经处理了.除非二维码的点位没有做上下料的验证
					String msg="数据错误，二维码点"+cDot.getDotNO()+"没有路由，AGV在没有传输退回二维码后没有去路";
					logger.error(msg);
					MsgCore.sendMsg(agv.getAgvId(),msg);
				}
			}
		}else if(cDot.getDotType()==DotTypeEnum.Normal) {
			//普通点位，按需求处理
			iDotEvent.arrivedNormal(agv,cDot);
		}else if(cDot.getDotType()==DotTypeEnum.PowerCheck) {
			//电量判断
			iDotEvent.arrivedPowerCheck(agv, data, cDot ); 
		} 
	}
	 
	

	/**
	 * 二维码处选择下道工序可用的机台，AGV并移动
	 * @param nextProcess 下一道工序
	 * @param qrdotno 当前的二维码
	 * @return
	 */
	 
	boolean toNextProcess(AgvBean agv, int specialline,String nextProcess,int qrdotno) {
		
		DeviceGroupDao exist=iDevice.findLockDeviceForAgv(agv.getAgvId(),nextProcess);
		//存在曾经锁过的机台
		if(exist!=null) {
			RoadDeviceDao roadDao=roadMapper.getTransRoad(qrdotno, exist.getDotno()); 
			if (roadDao != null && roadDao.getRoad() > 0) {
				agv.setNextRoad(roadDao.getRoad());
				agv.setNextDeviceid(exist.getDeviceid());
				agv.setNextProcess(nextProcess );
				agv.setNextNode(String.format("%s",roadDao.getDevicegroup() ));
				agv.setFinishTrans(false);
				agv.setUpHasTrans(false);
				agv.setDownHasTrans(false);
				iAgv.saveCache(agv);
				iAgvEvent.toRoad(agv,  roadDao.getRoad());
				
				//iDevice.lockDevice(exist.getDeviceid(), agv.getAgvId());
				//iDevice.unlockOtherDevice(exist.getDeviceid(), agv.getAgvId());
				
				iDevice.lockDeviceForTrans(exist.getDeviceid(), agv.getAgvId());
				
				//重置机台传输数据
				DeviceCmdMap.clearState(exist.getDeviceid());
				
				return true;
			} else {
				// 没有路线，数据上的错误 -1
				logger.error(String.format("%d 到 %d 没有路线？？", qrdotno, exist.getDotno()));
				return false;
				 
			} 
		} 
		DeviceGroupDao gbean =iDevice.findNextDevice (agv.getAgvId(),  nextProcess ,   specialline )  ;
		if (gbean == null) { 
			// 没有机台，需要等下一个周期  
			 MsgCore.sendMsg(agv.getAgvId(), String.format("%s没有可用的机台，等待中",Tools.getProcess(nextProcess)));
			return false;
		} else {  
			RoadDeviceDao roadDao=roadMapper.getTransRoad(qrdotno, gbean.getDotno());  
			if (roadDao != null && roadDao.getRoad() > 0) {
				 
				//目标机台 机台上锁
				//iDevice.lockDevice(gbean.getDeviceid(), agv.getAgvId());
				//iDevice.unlockOtherDevice(gbean.getDeviceid(), agv.getAgvId());
				iDevice.lockDeviceForTrans(gbean.getDeviceid(), agv.getAgvId());
				
				//重置机台传输数据
				DeviceCmdMap.clearState(gbean.getDeviceid());
				
				MsgCore.sendMsg(agv.getAgvId(),"目标机台:"+roadDao.getDevicegroup());
				
				//行驶
				agv.setNextDeviceid(gbean.getDeviceid());
				agv.setNextRoad(roadDao.getRoad());
				agv.setNextNode(String.format("%s",roadDao.getDevicegroup() ));
				agv.setNextProcess(nextProcess );
				iAgv.saveCache(agv);
				iAgvEvent.toRoad(agv,  roadDao.getRoad());
				return true;
			} else {
				// 没有路线，数据上的错误 -1
				MsgCore.sendMsg(agv.getAgvId(),String.format("%d 到 %d 没有路线？", qrdotno, gbean.getDotno()));
				logger.error(String.format("%d 到 %d 没有路线？", qrdotno, gbean.getDotno()));
				 return false;
			}
		}
	}
	
 
	
	 
	/**
	 * AGV停止了
	 * @param agv
	 */
	void agvOffline(AgvBean agv ,AgvDataBean data) {
		if(agv.getAreaId()>0) {
			//如果有交管区
			long areaID = iControlArea.getAreaId(data.getX(), data.getY());
			if(areaID==0 || agv.getAreaId()!=areaID) {
				//离开了事发的交管区,需要处理之前的交管
				areaID=agv.getAreaId();
				agv.setAreaId(0);  
				iControlArea.removeStopAgv(areaID, agv.getAgvId());
			}
		}
		agv.setNextNode("");
		iAgv.saveCache(agv);
	}
	
	/**
	 * 充电判断 true= 不用做后面的操作 (202号点）
	 * @param agv
	 * @return
	 */
	boolean toPower202(AgvBean agv,AgvDataBean data,int dotno ) {
		if(data.getPower()<=70 ) {
			//电量低于60并且充电通道没有被锁去充电桩
			int pre=iPower.agvToPower(dotno, agv);
			if(pre==1) {
				//去充电桩成功，就不需要后面的操作了
				return true;
			} else {
				if(data.getPower()<=50) {
					MsgCore.sendMsg(agv.getAgvId(), "电量不足，也没有可用的充电桩");
					return true;
				}else {
					//50-60之间，还能跑一会儿
				} 
			} 
		}
		return false;
	}
	
	
 
}
